/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import com.bourre.data.collections.Map;
import com.bourre.data.iterator.Iterator;
import com.bourre.events.EventBroadcaster;
import com.bourre.events.EventType;

import fever.app.theme.Theme;
import fever.app.theme.ThemeEvent;
import fever.events.FeverController;
import fever.exception.IllegalArgumentException;
import fever.Fever;
import fever.utils.Stringifier;

/**
 * Locator and manager for Application look and Feel.
 * 
 * <p>Broadcasts {@link #onChangeEVENT} event type when theme change.
 * 
 * <p>Example using <strong>AsWing</strong> Look'n Feel.
 * {@code
 *   var at : ThemeLocator = ThemeLocator.getInstance();
 *   at.register( 'Office 2003', new Office2003LAF() );
 *   at.register( 'WinXP 2003', new WinXPLAF() );
 *   at.addEventListener( ThemeLocator.onChangeEVENT, this, _onUIUpdate );
 *   at.load( 'WinXP 2003' );
 * }
 * 
 * <p>Look and Feel is really apply using custom process.<br />
 * Associates {@code Command} to 
 * {@link fever.events.FeverEventList#onApplyLookAndFeelEVENT} event type.<br />
 * A concrete command example is available in {@link fvaswing.commands.FvApplyLookAndFeel}
 * 
 * @see fever.app.theme.Theme
 * @see fvaswing.components.FvThemeChooser
 * @see fvaswing.commands.FvApplyLookAndFeel
 * 
 * @author Romain Ecarnot
 */
class fever.app.theme.ThemeLocator
{	
	//-------------------------------------------------------------------------
	// Events definition
	//-------------------------------------------------------------------------
	
	/** Event type broadcasted when theme change. */
	public static var onChangeEVENT : EventType = ThemeEvent.onChangeEVENT;
	
	
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var _instance : ThemeLocator;
	private var _map : Map;
	private var _oEB : EventBroadcaster;
	private var _current : Theme;
	
	
	//-------------------------------------------------------------------------
	// Public Properties
	//-------------------------------------------------------------------------
	
	/** Property name identifier for sharedObject saving. */
	public static function get CONFIG_ID() : String { return 'theme'; }
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Returns unique {@link FvThemeLocator} instance.
	 */
	public static function getInstance() : ThemeLocator
	{
		if( _instance == undefined ) _instance = new ThemeLocator();
		return _instance;
	}
	
	/**
	 * Registers a new available {code lafInstance} LookAndFeel instance with passed-in 
	 * {@code lafName} id.
	 * 
	 * @return registred Look'n Feel
	 * @throws IllegalArgumentException if {@code lafName} or {@code lafInstance} are 
	 * {@code undefined} or {@code null}
	 */
	public function register( lafName : String, lafInstance : Object, previewLink : String ) : Boolean
	{
		if( !lafName || !lafInstance ) 
		{
			throw new IllegalArgumentException( 'laf name and class must be defined' );
			return false;	
		}
		
		if( !_map.containsKey( lafName ) )
		{
			_map.put( lafName, new Theme( lafName, lafInstance, previewLink ) );
			return true;
		}
		
		return false;
	}
	
	/**
	 * Registers passed-in {@link fever.app.theme.Theme} theme.
	 */
	public function registerTheme( theme : Theme ) : Boolean
	{
		if( !theme ) return false;
		
		if( !_map.containsKey( theme.getName() ) )
		{
			_map.put( theme.getName(), theme );
			return true;
		}
		
		return false;
	}
	
	/**
	 * Returns {@code true} if a theme is registred with passed-in {@code lafName} 
	 * ID.
	 */
	public function isRegistred( lafName : String ) : Boolean
	{
		return _map.containsKey( lafName );	
	}
	
	/**
	 * Unregisters passed-in {@code lafName} Look And Feel from theme map.
	 */
	public function unregister( lafName : String ) : Boolean
	{
		if( _map.containsKey( lafName ) )
		{
			var laf = _map.get( lafName );
			laf = undefined; delete laf;
			return true;
		}
		return false;
	}
	
	/**
	 * Applies passed-in {@code lafName} Look'n Feel.
	 * 
	 * <p>LAF must be registred before with {@link #register()} method.
	 * 
	 * <p>If a theme configuration is saved, loads saved theme.
	 * 
	 * <p>Look and Feel is really apply using custom process.<br />
	 * Associated {@code Command} to 
	 * {@link fever.events.FeverEventList#onApplyLookAndFeelEVENT} event type<br />
	 * A concrete command example is available in {@link fvaswing.commands.FvApplyLookAndFeel}
	 * 
	 * @return {@code true} if LAF is correctly applied.
	 */
	public function load( lafName : String ) : Boolean
	{
		if( !lafName ) lafName = Fever.application.config.load( CONFIG_ID );
		
		if( _map.containsKey( lafName ) )
		{
			var o : Theme = Theme( _map.get( lafName ) );
			
			FeverController.getInstance().broadcastEvent( new ThemeEvent( ThemeEvent.onApplyLookAndFeelEVENT, o ) );
			
			_oEB.broadcastEvent( new ThemeEvent( onChangeEVENT, o ) );
			
			_current = o;
			
			Fever.application.config.save( ThemeLocator.CONFIG_ID, _current.getName() );
			
			return true;
		}
		
		return false;
	}
	
	/**
	 * Returns registred theme names list.
	 */
	public function getThemeList() : Array
	{
		var it : Iterator = _map.getKeysIterator();
		var a : Array = new Array();
		
		while( it.hasNext() ) a.push(  it.next() );
		a.sort();
		return a;
	}
	
	/**
	 * Returns Look'n Feel regitred with passed {@code lafName} id. 
	 */
	public function getTheme( lafName : String ) : Theme
	{
		if( _map.containsKey( lafName ) )
		{
			return _map.get( lafName );
		}
		return null;
	}
	
	/**
	 * Returns current application theme.
	 * 
	 * <p>If laf is registred in map, returns {@link fever.app.theme.Theme} instance.
	 */
	public function getCurrentTheme() : Theme
	{
		return _current;
	}
	
	/**
	 * Adds passed-in {@code listener} for receiving passed-in {@code type} event type.
	 */
	public function addEventListener( type : EventType, listener ) : Void
	{
		_oEB.addEventListener.apply( _oEB, arguments );	
	}
	
	/**
	 * Removes passed-in {@code listener} that suscribed for passed-in {@code type} event.
	 */
	public function removeEventListener( type : EventType, listener ) : Void
	{
		_oEB.removeEventListener( type, listener );
	}
	
	/**
	 * Removes all theme listeners.
	 */
	public function clearListener() : Void
	{
		_oEB.removeAllListeners();
	}
	
	/**
	 * Returns string representation.
	 */
	public function toString() : String
	{
		return Stringifier.parse( this );
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	/**
	 * Constructs a new {@code FvThemeLocator} instance.
	 * 
	 * <p>Constructor is private to avoid runtime instanciation.
	 */
	private function ThemeLocator()
	{
		_map = new Map();
		_oEB = new EventBroadcaster( this );
	} 
}	
